
/**
 * @file Rinex3ObsData.hpp
 * Encapsulate RINEX observation file data, including I/O
 */

#ifndef RINEX3OBSDATA_HPP
#define RINEX3OBSDATA_HPP

#include <vector>
#include <list>
#include <map>

#include "CommonTime.hpp"
#include "FFStream.hpp"
#include "Rinex3ObsBase.hpp"
#include "Rinex3ObsHeader.hpp"
#include "RinexDatum.hpp"

namespace gpstk
{

      /// @ingroup FileHandling
      //@{

      /// This class models a RINEX Observation Data Record.
      /// @sa gpstk::Rinex3ObsStream and gpstk::Rinex3ObsHeader.
      /// @sa rinex_obs_test.cpp and rinex_obs_read_write.cpp for examples.
   class Rinex3ObsData : public Rinex3ObsBase
   {
   public:

         /// Constructor.
      Rinex3ObsData();

         /// Destructor
      virtual ~Rinex3ObsData() {}


         /// Map from SatID to RinexDatum; order of the data matches the
         /// order of RinexObsIDs in the header
      typedef std::map<SatID, std::vector<RinexDatum> > DataMap;

         /// Time corresponding to the observations
      CommonTime time;


         /// Epoch flag has the following values
         /// 0 ok
         /// 1 power failure since previous epoch
         /// 2 start moving antenna
         /// 3 new site occupation (end moving antenna)
         ///   at least MARKER NAME header record follows
         /// 4 header records follow
         /// 5 external event
         /// 6 cycle slip record - same format as observation, but slips not data,
         ///   and LLI and SSI are blank
      short epochFlag;


         /// Number of satellites in this observation, except when
         /// epochFlag=2-5, then number of auxiliary header records to follow.
      short numSVs;


      double clockOffset;        ///< optional clock offset in seconds

      DataMap obs;               ///< the map of observations

      Rinex3ObsHeader auxHeader; ///< auxiliary header records (epochFlag 2-5)


         /** This method returns the RinexDatum of a given observation
          *
          * @param sat     Satellite whose observation we want to fetch.
          * @param index   Index representing the observation type. It is
          *                obtained from corresponding RINEX Observation Header
          *                using method 'Rinex3ObsHeader::getObsIndex()'.
          */
      virtual RinexDatum getObs( const SatID& svID, size_t index ) const
         noexcept(false);


         /** This method returns the RinexDatum of a given observation
          *
          * @param sat  Satellite whose observation we want to fetch.
          * @param type String representing the observation type.
          * @param hdr  Rinex3ObsHeader for current RINEX file.
          */
      virtual RinexDatum getObs( const SatID& svID,
                                 const std::string& obsID,
                                 const Rinex3ObsHeader& hdr ) const
         noexcept(false);

         /** This method returns the RinexDatum of a given observation
          *
          * @param svID  SatID of satellite
          * @param obsID RinexObsID  of the observation type.
          * @param hdr   Rinex3ObsHeader for current RINEX file.
          */
      virtual RinexDatum getObs( const SatID& svID,
                                 const RinexObsID& obsID,
                                 const Rinex3ObsHeader& hdr ) const
         noexcept(false);

         /** This sets the RinexDatum for a given observation
          *
          * @param data  RinexDatum of obs
          * @param svID  SatID of satellite
          * @param obsID RinexObsID  of the observation type.
          * @param hdr   Rinex3ObsHeader for current RINEX file.
          */
      virtual void setObs( const RinexDatum& data,
                           const SatID& svID,
                           const RinexObsID& obsID,
                           const Rinex3ObsHeader& hdr )
         noexcept(false);

         /// A Debug output function.
         /// Dumps the time of observations and the IDs of the Sats
         /// in the map.
      virtual void dump(std::ostream& s) const;


         /// Another dump, using information from the header
      void dump(std::ostream& s, Rinex3ObsHeader& head) const;

      std::string timeString() const
         noexcept(false);

   protected:


         /// Writes a correctly formatted record from this data to stream \a s.
         /// When printing comment records, you'll need to format them
         /// correctly yourself. This means making sure that "COMMENT" is at
         /// the end of the line and that they're the correct length
         /// (<= 80 chrs).
         /// Also, make sure to correctly set the epochFlag to the correct
         /// number for the type of header data you want to write.
      virtual void reallyPutRecord(FFStream& s) const
         noexcept(false);


         /** This functions obtains a RINEX 3 Observation record from the given
          *  FFStream. If there is an error in reading from the stream, it is
          *  reset to its original position and its fail-bit is set.
          *
          *  Because of the Rinex 3 Obs format, a Rinex3ObsData record returned
          *  might not have data.  Check the Rinex3SatMap for empty() before
          *  using any data in it.
          *
          *  @throws StringException When a StringUtils function fails
          *  @throws FFStreamError   When exceptions(failbit) is set and a read
          *          or formatting error occurs.  Also resets the stream to its
          *          pre-read position.
          */
      virtual void reallyGetRecord(FFStream& s)
         noexcept(false);


   private:


         /// Writes the CommonTime into RINEX 3 format.
         /// If it's a bad time, it will return blanks.
      std::string writeTime(const CommonTime& dt) const
         noexcept(false);


         /** This function constructs a CommonTime object from the given
          *  parameters.
          *
          * @param line The encoded time string found in the RINEX record.
          * @param hdr  The RINEX Observation Header object for the current
          *             RINEX file.
          */
      CommonTime parseTime( const std::string& line,
                            const Rinex3ObsHeader& hdr,
                            const TimeSystem& ts) const
         noexcept(false);


   }; // End of class 'Rinex3ObsData'

      //@}

} // End of namespace gpstk

#endif   // GPSTK_RINEX3OBSDATA_HPP
